
import { spawnCreep } from "../../../role/spawn";
import { errLog, timerEnd, timerLog, timerStart } from "../../../util/log";
import { GroupTerminal } from "../terminal_group";
import { ClusterColony, structureClusterTemplates } from "./room/colony";
import { StructureMap, StructurePlan } from "../../../util/structure_map";
import { getActualFreeCapacity, getActualResourceAmount, transportResource } from "../../../util/store";
import { jobGoto } from "../../../job/goto";

export class ClusterCore extends GroupTerminal{
    /**
     * 
     * @param {Room} spawnRoom 
     * @param {RoomPosition} corePos 
     */
    constructor(spawnRoom = undefined, corePos = undefined){
        super(spawnRoom);
        if(spawnRoom !== undefined){
            this.corePos = corePos;
        }
    }
    serialize(){
        return {
            super: super.serialize(),
            corePos: this.corePos,
        }
    }
    deserialize(obj){
        super.deserialize(obj.super);
        this.corePos = obj.corePos;
        return this;
    }
    get maxSize(){
        return this.isActive()?4:0;
    }
    get size(){
        let ans = 0;
        for(let creep of this.creeps){
            if(!creep){
                continue;
            }
            ans++;
        }
        return ans;
    }
    isActive(){
        return this.spawnRoom.controller.level>=5 && this.corePos != undefined;
    }
    spawn(){
        if(!this.isActive()){
            errLog(new Error("core not active"));
            return ERR_BUSY;
        }
        let i;
        for(i=0; i<this.maxSize; i++){
            if(!Game.creeps[this.creepNames[i]]){
                break;
            }
        }
        if(i>=this.maxSize){
            errLog(new Error("excessive core size"));
            return ERR_FULL;
        }
        
        let newCreep = spawnCreep(this.spawnRoom, {
            require: [MOVE, CARRY, CARRY, CARRY, CARRY],
        }, {
            memory: {
                role: "core",
            }
        }, 250);

        if(!(newCreep instanceof Creep)){
            return newCreep;
        }
        
        this.creepNames[i] = newCreep.name;
        return OK;
    }
    addCreep(creep){
        let i;
        for(i=0; i<this.maxSize; i++){
            if(Boolean(Game.creeps[this.creepNames[i]])){
                break;
            }
        }
        if(i>=this.maxSize){
            errLog(new Error("core full"));
            return ERR_FULL;
        }
        this.creepNames[i] = creep.name;
        return OK;
    }
    get extensions(){

        if(this._extensions !== undefined){
            return this._extensions;
        }
        this.findStructures();
        return this._extensions;
    }
    get spawns(){

        if(this._spawns !== undefined){
            return this._spawns;
        }
        this.findStructures();
        return this._spawns;
    }
    get links(){

        if(this._links !== undefined){
            return this._links;
        }
        this.findStructures();
        return this._links;
    }
    get containers(){
        if(this._containers !== undefined){
            return this._containers;
        }
        this.findStructures();
        return this._containers;
    }
    get transferTargets(){
        if(this._transferTarget !== undefined){
            return this._transferTarget;
        }
        let targets = [...this.extensions, ...this.spawns]
            .filter((target)=>getActualFreeCapacity(target)>0);
        return this._transferTarget = targets;
    }

    findStructures(){
        let colony = Game.groups[this.spawnRoomName];
        if(!(colony instanceof ClusterColony)){
            throw new Error("no colony found");
        }
        let coreMap = colony.map.lookAt(this.corePos.x, this.corePos.y)
        if(coreMap){
            coreMap = coreMap[0];//todo: find core instead of assuming it is
        }
        if(!(coreMap instanceof StructureMap)){
            throw new Error("can't find core map");
        }

        let extensions = [];
        let links = [];
        let spawns = [];
        let containers = [];
        coreMap.forEachPos((cluster,x,y)=>{
            let structure = this.spawnRoom
                .getPositionAt(x+this.corePos.x, y+this.corePos.y)
                .lookFor(LOOK_STRUCTURES)
                .find((struct)=>struct.structureType === cluster.structureType);
            if(!structure){
                return;
            }

            switch(structure.structureType){
                case STRUCTURE_EXTENSION:
                    extensions.push(structure);
                    break;
                case STRUCTURE_LINK:
                    links.push(structure);
                    break;
                case STRUCTURE_SPAWN:
                    spawns.push(structure);
                    break;
                case STRUCTURE_CONTAINER:
                    containers.push(structure);
                    break;
                default:
                    break;
            }
        });
        // let looks = this.spawnRoom.lookForAtArea(LOOK_STRUCTURES, this.corePos.y-2, this.corePos.x-2, this.corePos.y+2, this.corePos.x+2, true);
        // for(let look of looks){
        //     let structure = look.structure;
        //     switch(structure.structureType){
        //         case STRUCTURE_EXTENSION:
        //             extensions.push(structure);
        //             break;
        //         case STRUCTURE_LINK:
        //             links.push(structure);
        //             break;
        //         case STRUCTURE_SPAWN:
        //             spawns.push(structure);
        //             break;
        //         case STRUCTURE_CONTAINER:
        //             containers.push(structure);
        //             break;
        //         default:
        //             break;
        //     }
        // }
        this._extensions = extensions;
        this._spawns = spawns;
        this._containers = containers;
        this._links = links;
    }

    run(){
        if(!this.isActive()){
            // errLog(new Error("core isn't active"));
            return ERR_BUSY;
        }

        let links = this.links;
        let transferTargets = this.transferTargets
            .filter((target)=>getActualFreeCapacity(target)>0);
        let withdrawTargets = links;
        let containers = this.containers;

        if(transferTargets.length === 0){
            transferTargets = containers
                .filter((container)=>getActualFreeCapacity(container)>0);
        }else{
            withdrawTargets = [...links, ...containers].filter((target)=>getActualResourceAmount(target)>0);
        }

        for(let i=0; i<this.maxSize; i++){
            let creep = Game.creeps[this.creepNames[i]];
            if(!Boolean(creep)){
                continue;
            }
            try{
                let spawn = this.spawns.find((structure)=>creep.pos.isNearTo(structure.pos));
                let targetPos = new RoomPosition(this.corePos.x+((i&0b01)>0?(-1):1), this.corePos.y+((i&0b10)>0?1:(-1)),this.spawnRoomName);
                this.creepRun(creep, withdrawTargets, transferTargets, spawn, targetPos);
            }catch(e){
                errLog(e);
            }
        }
    }
    /**
     * 
     * @param {AnyCreep} creep 
     * @param {(StructureSpawn|StructureExtension)[]} transferTargets 
     * @param {StructureLink[]} links 
     * @param {StructureContainer[]} containers 
     * @param {StructureSpawn} spawn 
     */
    creepRun(creep, withdrawTargets, transferTargets, spawn, targetPos){
        
        withdrawTargets = withdrawTargets
            .filter((target)=>target.pos.isNearTo(creep));

        for(let target of withdrawTargets){
            if(getActualFreeCapacity(creep) === 0){
                break;
            }
            if(OK === transportResource(target, creep, undefined, undefined, true)){
                break;
            }
        }
        for(let target of transferTargets){
            if(getActualResourceAmount(creep) === 0){
                break;
            }
            if(OK === transportResource(creep, target, undefined, undefined, true)){
                break;
            }
        }
        if(creep.ticksToLive<1380 && spawn){//1500-Math.floor(600/creep.body.length) -> creep.body.length === 5
            spawn.renewCreep(creep);
        }
        if(!creep.pos.isEqualTo(targetPos)){
            jobGoto.run(creep, [{pos: targetPos}]);
        }
        return OK;
    }
}